﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Windows;
using System.Linq;
using Xceed.Silverlight.Data;
using System.Collections.Generic;

namespace Xceed.Silverlight.DataGrid
{
  internal class LayoutUpdateVisitor : VisitorBase
  {
    public LayoutUpdateVisitor( 
      DataGridControl datagrid, 
      IEnumerable<DataPath> items, 
      bool isLoading, 
      LayoutFactoryBase layoutFactory )
      : base( ChildrenPolicy.LocalyHandled, false )
    {
      if( datagrid == null )
        throw new ArgumentNullException( "datagrid" );

      if( items != null )
      {
        m_items = items.GetEnumerator();
        m_hasMoreData = m_items.MoveNext();
      }

      m_datagrid = datagrid;
      m_isLoading = isLoading;
      m_layoutFactory = layoutFactory;
    }

    public override void Visit( VirtualRootContainer container )
    {
      this.VisitContainer( ( VirtualContainerBase )container );
    }

    public override void Visit( VirtualGroupContainer container )
    {
      this.VisitContainer( ( VirtualContainerBase )container );

      // When group is collapsed : no more data
      if( !m_datagrid.DataGridContext.IsGroupExpanded( new DataGridPath( container.Path ) ) )
      {
        m_hasMoreData = m_items.MoveNext();
      }
    }

    public override void Visit( DataRow dataRow )
    {
      m_hasMoreData = m_items.MoveNext();

      m_isRowAlternate = m_isRowAlternate.GetValueOrDefault( dataRow.IsAlternate );
      dataRow.IsAlternate = m_isRowAlternate.Value;
      m_isRowAlternate = !m_isRowAlternate.Value;
      // Only update rows. No need to update row's cells.
      dataRow.SetRowCommonStateOnly( m_datagrid.ResolveRowCommonState( dataRow.IsSelected, false, dataRow.IsAlternate ), false );
    }

    private void VisitContainer( VirtualContainerBase container )
    {
      DataPath containerPath = container.Path;
      bool containRows = ( containerPath.Depth == m_datagrid.GroupDescriptions.Count );
      List<ILayoutElement> children = new List<ILayoutElement>();

      LinkedList<ILayoutElement> stubElements = null;
      if( m_isLoading && containRows )
      {
        IEnumerable<ILayoutElement> stubList =
          container.Elements
          .Where( ( e ) => ( ( RowWrapper )e ).Row.Path.LastChild is StubDataItem );

        stubElements = new LinkedList<ILayoutElement>( stubList );
      }

      while(m_hasMoreData && containerPath.IsAncestorOf( m_items.Current ))
      {
        DataPath itemPath = m_items.Current.CreateAncestorOrSamePath( containerPath.Depth + 1 );


        ILayoutElement element = null;
        DataPath exceptionPath = m_datagrid.DataGridContext.GetActualDataPathOrDataPath(m_datagrid.CurrentPath);

        Func<bool> tryReuseStubElements = () =>
        {
          if( stubElements == null || stubElements.Count == 0 )
            return false;

          element = stubElements.First.Value;
          stubElements.RemoveFirst();
          m_datagrid.RootHost.ReuseElement( itemPath, element );
          return true;
        };


        // 1.Reuse already present child, if possible.
        // 2.Use the first stub item found
        // 3.Use a recycled item
        // 4.Create a new one.
        if( !m_datagrid.RootHost.TryGetElement( itemPath, out element )
          && ( !m_isLoading || !tryReuseStubElements() )
          && !m_datagrid.RootHost.TryGetRecycledElement( itemPath, exceptionPath, out element ) )
        {
          element = m_datagrid.CreateAndPrepareElement( itemPath );
        }

        m_datagrid.AttachElementInternalCore( element, itemPath );
        ( ( IVisitorElement )element ).Accept( this );

        children.Add( element );
      }

      this.SetInheritedContexts( container, children );
      LayoutUpdateResult result = container.SetLayout( m_layoutFactory.CreateLayout( container, children ) );

      foreach( ILayoutElement element in result.AddedElements )
      {
        m_datagrid.RootHost.AddElement( element );
      }

      foreach( ILayoutElement element in result.RemovedElements )
      {
        m_datagrid.ReleaseElement( element );
      }
    }

    private void SetInheritedContexts( VirtualContainerBase container, List<ILayoutElement> children )
    {
      foreach( ILayoutElement element in children )
      {
        if( element is VirtualGroupContainer )
          continue;

        UIElement uiElement = ( ( WrapperBase )element ).Child;

        m_datagrid.ModifyGroupContextForElement( uiElement, DataGridControl.GetGroupContext( container ) );
        m_datagrid.ModifyStatContextForElement( uiElement, DataGridControl.GetStatContext( container ) );
      }
    }

    private DataGridControl m_datagrid;
    private IEnumerator<DataPath> m_items; //null
    private bool m_hasMoreData; //false
    private bool? m_isRowAlternate = null;
    private bool m_isLoading;
    private LayoutFactoryBase m_layoutFactory;
  }
}
